/*
Copyright [2012] [Maria Eugenia Malagamba]
Copyright [2012] [Leandro Pellegrino]
Copyright [2012] [Julia Gambuzzi]
Copyright [2012] [Ignacio Capodanno]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

#pragma once
#include "../Entidades/Pintable.h"
#include <vector>
#include "SDL.h"
#include "Figuras/Rectangulo.hpp"
#include "Figuras/Figura.hpp"
#include "Figuras/Circulo.hpp"
#define PI 3.141592653589793
using namespace Entidades;

class Colisionable : public Pintable{

protected:

	Colisionable(): Pintable(){}
	Colisionable(string pathDeLaImagen, int poderDeDestruccion = 10) 
		: Pintable(pathDeLaImagen){
			this->energiaPasoACero = false;
			this->poderDeDestruccion = this->poderDeDestruccion;
	}

	Colisionable(double posicionX, double posicionY, string pathDeLaImagen, int anchoDeLaImagen,
		int altoDeLaImagen, double rotacionDeLaImagen, int poderDeDestruccion = 10)
		: Pintable(posicionX,posicionY,pathDeLaImagen,anchoDeLaImagen,altoDeLaImagen, rotacionDeLaImagen){
			this->energiaPasoACero = false;
			this->poderDeDestruccion = poderDeDestruccion;
	}

	Colisionable(double posicionX, double posicionY, string pathDeLaImagen, int anchoDeLaImagen,
		int altoDeLaImagen,double rotacionDeLaImagen, int cantidadFrames, int poderDeDestruccion)
		: Pintable(posicionX,posicionY,pathDeLaImagen,anchoDeLaImagen,altoDeLaImagen,rotacionDeLaImagen,cantidadFrames){
			this->energiaPasoACero = false;
			this->poderDeDestruccion = poderDeDestruccion;
	}

	bool energiaPasoACero;
	int poderDeDestruccion;
	double altoPantalla;
	double anchoPantalla;

public:

	// supongo siempre un contorno rectangular
	virtual bool hayColisionDeContornoCon(Colisionable * otroColisionable){

		Rectangulo * bordeDelColisionable = new Rectangulo(this->getPosicionX(), this->getPosicionY(), this->getAnchoDeLaImagen(), this->getAltoDeLaImagen());
		Rectangulo * bordeDelOtroColisionable = new Rectangulo(otroColisionable->getPosicionX(), otroColisionable->getPosicionY(), otroColisionable->getAnchoDeLaImagen(), otroColisionable->getAltoDeLaImagen());

		bool hayColision =	bordeDelColisionable->hayIntereseccion(bordeDelOtroColisionable);

		delete bordeDelColisionable;
		delete bordeDelOtroColisionable;
		return hayColision;
	}

	bool hayColisionCircularCon(Colisionable * otroColisionable){
		Circulo * bordeDelColisionable = new Circulo(this->getPosicionX(), this->getPosicionY(), this->getAnchoDeLaImagen());
		Circulo * bordeDelOtroColisionable = new Circulo(otroColisionable->getPosicionX(), otroColisionable->getPosicionY(), otroColisionable->getAnchoDeLaImagen());

		bool hayColision =	bordeDelColisionable->hayIntereseccion(bordeDelOtroColisionable);

		delete bordeDelColisionable;
		delete bordeDelOtroColisionable;
		return hayColision;
	}

	// colision a nivel pixel
	virtual bool hayColisionDeImagenCon(Colisionable * otroColisionable){

		//Busco la interseccion de las dos cajas donde encontre colision
		int techoA = this->getPosicionY();
		int pisoA = this->getPosicionY() + this->getAltoDeLaImagen();
		int izquierdaA = this->getPosicionX();
		int derechaA = this->getPosicionX() + this->getAnchoDeLaImagen();

		int techoB = otroColisionable->getPosicionY();
		int pisoB = otroColisionable->getPosicionY() + otroColisionable->getAltoDeLaImagen();
		int izquierdaB = otroColisionable->getPosicionX();
		int derechaB = otroColisionable->getPosicionX() + otroColisionable->getAnchoDeLaImagen();

		//Calculo el area de la interseccion de ambas cajas
		int interIzq = this->calcularMaximo(izquierdaA,izquierdaB);
		int interDer = this->calcularMinimo(derechaA, derechaB);
		int interTecho = this->calcularMaximo(techoA, techoB);
		int interPiso = this->calcularMinimo(pisoA,pisoB);

		//Achico las intersecciones al pedazo que esta dentro de la pantalla
		interIzq = this->calcularMaximo(interIzq,0);
		interDer = this->calcularMinimo(interDer, this->getAnchoPantalla());
		interTecho = this->calcularMaximo(interTecho, 0);
		interPiso = this->calcularMinimo(interPiso,this->getAltoPantalla());

		//Recorro los pixels del rect1 y cheque colisiones de pixeles con la superficie del rect2
		Uint32 pixelClave1 = 0;
		Uint32 pixelClave2 = 0;
		Uint32 pixelClave3=  this->getUltimaRotacion()->format->colorkey;
		Uint32 pixelClave4=  otroColisionable->getUltimaRotacion()->format->colorkey;
		Uint32 pixel1 = NULL;
		Uint32 pixel2 = NULL;

		for (int i = interTecho; i < interPiso; i ++){
			for (int j = interIzq; j < interDer ; j ++){
				//Calcular el offset de la interseccion para cada superficie
				pixel1 = this->getPixel(j - izquierdaA ,i - techoA); 
				pixel2 = otroColisionable->getPixel(j - izquierdaB ,i - techoB);

				if((pixel1 != pixelClave1 && pixel1 != pixelClave3) && (pixel2 != pixelClave2 && pixel2 != pixelClave4)){
					return true;
				}
			}
		}
		return false;	
	}


	bool hayColisionDeContornoConCirculo(Colisionable *otroColisionable, int radio){

		int posX = this->getPosicionX();
		int posY = this->getPosicionY();

		if (radio != ( this->getAltoDeLaImagen() /2 )){
			posX = this->getPosicionX() - radio + (this->getAltoDeLaImagen() /2 );
			posY = this->getPosicionY() - radio + (this->getAltoDeLaImagen() /2 );
		}

		Rectangulo * bordeDelColisionable = new Rectangulo(posX,posY , posX + radio*2, posY + radio*2);
		Rectangulo * bordeDelOtroColisionable = new Rectangulo(otroColisionable->getPosicionX(), otroColisionable->getPosicionY(), otroColisionable->getAnchoDeLaImagen(), otroColisionable->getAltoDeLaImagen());

		bool hayColision =	bordeDelColisionable->hayIntereseccion(bordeDelOtroColisionable);

		delete bordeDelColisionable;
		delete bordeDelOtroColisionable;
		return hayColision;
	}


	// colision a nivel pixel
	bool hayColisionDeImagenConCirculo(Colisionable * otroColisionable, int radio){

		int posX;// = this->getPosicionX();
		int posY;// = this->getPosicionY();

		if (radio != ( this->getAltoDeLaImagen() /2 )){
			posX = this->getPosicionX() - radio + (this->getAltoDeLaImagen() /2 );
			posY = this->getPosicionY() - radio + (this->getAltoDeLaImagen() /2 );
		}

		int centroX =  posX + radio ;
		int centroY =  posY + radio;

		int techoA = posY;
		int pisoA = posY + radio*2;
		int izquierdaA = posX;
		int derechaA =posX + radio*2;

		int techoB = otroColisionable->getPosicionY();
		int pisoB = otroColisionable->getPosicionY() + otroColisionable->getAltoDeLaImagen();
		int izquierdaB = otroColisionable->getPosicionX();
		int derechaB = otroColisionable->getPosicionX() + otroColisionable->getAnchoDeLaImagen();

		//Calculo el area de la interseccion de ambas cajas
		int interIzq = this->calcularMaximo(izquierdaA,izquierdaB);
		int interDer = this->calcularMinimo(derechaA, derechaB);
		int interTecho = this->calcularMaximo(techoA, techoB);
		int interPiso = this->calcularMinimo(pisoA,pisoB);

		//Recorro los pixels del rect1 y cheque colisiones de pixeles con la superficie del rect2
		Uint32 pixelClave1 = 0;
		Uint32 pixelClave2 = 0;
		Uint32 pixelClave3=  this->getUltimaRotacion()->format->colorkey;
		Uint32 pixelClave4=  otroColisionable->getUltimaRotacion()->format->colorkey;
		Uint32 pixel1 = NULL;
		Uint32 pixel2 = NULL;

		for (int i = interTecho; i < interPiso; i ++){
			for (int j = interIzq; j < interDer ; j ++){
				//Calcular el offset de la interseccion para cada superficie
				pixel2 = otroColisionable->getPixel(j - izquierdaB ,i - techoB);
				int distancia =  this->distancia(centroX, centroY, j, i);

				if((pixel2 != pixelClave2 && pixel2 != pixelClave4) && (distancia < radio)){
					return true;
				}
			}
		}
		return false;	
	}

	virtual bool hayColisionEnElFuturoCon(Colisionable * otroColisionable) { return false; };

	virtual int colisionar(int danio){ return 0; };

	virtual Colisionable* colisionarAsteroide(){return NULL;}

	virtual ~Colisionable()	{}

	void setEnergiaPasoACero(bool estado){
		this->energiaPasoACero = estado;
	}

	bool getEnergiaPasoACero(){
		return this->energiaPasoACero;
	}

	int getPoderDeDestruccion(){
		return this->poderDeDestruccion;
	}

protected:
	// cálculos de colision x pixel
	int calcularMaximo(int x, int y){
		return ((x > y) ? x : y);
	}

	int calcularMinimo(int x, int y){
		return ((x < y) ? x : y);
	}

	int distancia (int x1, int y1, int x2, int y2){
		int x = abs( x2 - x1 );  // x = valor absoluto de x2  x1, es decir, la distancia entre las x
		int y = abs( y2 - y1 );  // y = valor absoluto de y2  y1, es decir, la distancia entre las y

		int min = x < y? x : y; // Si x < y min = x, si no, vale y. Es decir, el menor de los 2

		return ( x+y - (min>>1) - (min>>2) + (min>>4) );
	}

public:
	Uint32 getPixel(int x, int y){

		//assert( !((x < surface->w) || (y < surface->h)) );
		int bpp = this->getUltimaRotacion()->format->BytesPerPixel;
		/* Here p is the address to the pixel we want to retrieve */
		Uint8 *p = (Uint8 *)this->getUltimaRotacion()->pixels + y * this->getUltimaRotacion()->pitch + x * bpp;

		switch(bpp) {
		case 1:
			return *p;

		case 2:
			return *(Uint16 *)p;

		case 3:
			if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
				return p[0] << 16 | p[1] << 8 | p[2];
			else
				return p[0] | p[1] << 8 | p[2] << 16;
		case 4:
			return *(Uint32 *)p;

		default:
			return 0;       /* shouldn't happen, but avoids warnings */
		}
	}

private:
	void setAltoPantalla(double alto){
		this->altoPantalla = alto;
	}

	double getAltoPantalla(){
		double alto = 480;
		return alto;
	}

	void setAnchoPantalla(double ancho){
		this->anchoPantalla = ancho;
	}

	double getAnchoPantalla(){
		double ancho = 640;
		return ancho;
	}

};
